package p13_cglib代理原理;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;

/**
 * cglib生成的代理类的结构
 * cglib可以不经过反射调用目标方法，使用的是FastClass，下一节介绍FastClass
 * MethodProxy在被使用时，才创建FastClass
 * 一个MethodProxy对应两个FastClass，一个结合目标，一个结合代理
 * 而一旦FastClass创建好，后续即可直接使用，而无需走反射
 * JDK代理每调用一次代理类的方法，就走一次反射(当反射次数达到17次，也会生成代理)
 */
public class ProxyClass extends Target {
    //回调方法使用cglib提供的函数式接口
    private MethodInterceptor methodInterceptor;
    //cglib接收的回调函数接口：MethodInterceptor，aop的通知也有个MethodInterceptor接口，不要弄混了
    public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    private static Method save0;
    private static Method save1;
    private static Method save2;
    private static MethodProxy save0Proxy;
    private static MethodProxy save1Proxy;
    private static MethodProxy save2Proxy;
    static {
        try {
            save0 = Target.class.getMethod("save");
            save1 = Target.class.getMethod("save", int.class);
            save2 = Target.class.getMethod("save", long.class);
            /**
             * 额外创建目标方法的代理对象FastClass，创建出来后使用目标方法就可以不用走反射
             * 有五个参数:
             *  1.目标类 2.代理类 3.目标方法前面 4.代理类中增强方法名称 5.代理类中原始方法名称
             */
            save0Proxy = MethodProxy.create(Target.class, ProxyClass.class, "()V", "save", "saveSuper");
            save1Proxy = MethodProxy.create(Target.class, ProxyClass.class, "(I)V", "save", "saveSuper");
            save2Proxy = MethodProxy.create(Target.class, ProxyClass.class, "(J)V", "save", "saveSuper");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    /**
     * 带原始功能的方法(没有任何增强，因为增强已经在回调函数中实现了)
     * MethodProxy结合代理类(当前这个代理类)使用时，直接调用的就是这是方法
     */
    public void saveSuper(){
        //直接调用父类的对应方法，从而到达调用目标类方法
        super.save();
    }
    public void saveSuper(int a){
        super.save(a);
    }
    public void saveSuper(long a){
        super.save(a);
    }

    /**
     * 带增强功能的方法
     */
    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    @Override
    public void save(int a) {
        try {
            methodInterceptor.intercept(this, save1, new Object[]{a}, save1Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    @Override
    public void save(long a) {
        try {
            methodInterceptor.intercept(this, save2, new Object[]{a}, save2Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
